package com.wanlian.router.net.internal;

import android.content.Context;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.text.TextUtils;
import android.util.Log;

import com.google.gson.Gson;
import com.smart.android.utils.Logger;
import com.wanlian.router.net.storage.HttpStorage;
import com.wanlian.router.storage.AppData;

import java.io.File;
import java.io.InputStream;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;

/**
 * Created by Hyu on 2016/7/11.
 * Email: fvaryu@qq.com
 */

public abstract class BaseHttpClient {

    public static final String TAG = "tag-net";
    private static final int TIMEOUT = 5 * 1000;
    private final Gson mGson = new Gson();
    private OkHttpClient mOkHttpClient;
    private Platform mPlatform = Platform.get();
    private String domain;
    private boolean sslEnable;
    private boolean printLog = false;

    public BaseHttpClient() {
        this(false);
    }

    public BaseHttpClient(boolean sslEnable) {
        this.sslEnable = sslEnable;
    }

    public void initByHttps(InputStream serverInput, InputStream clientInput, String password) {
        init(new Builder(serverInput, clientInput, password)
                .setSslEnable(sslEnable));
    }

    public void initByHttp() {
        init(new Builder());
    }

    public void init(Builder builder) {
        mOkHttpClient = builder.build();
        domain = builder.domain;
        this.printLog = builder.logger;
    }

    public OkHttpClient getOkHttpClient() {
        return mOkHttpClient;
    }

    private Headers headers() {
        Map<String, String> map = addHeaders();
        Headers.Builder builder = new Headers.Builder();
        if (map != null) {
            for (String key : map.keySet()) {
                String value = map.get(key);
                if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) {
                    continue;
                }
                builder.add(key, value);
            }
        }
        return builder.build();
    }

    public final void toPost(Context context, boolean isShow, String relativeUrl, RequestParams params,
                             IParser iParser/*如果不需要解析数据，传null*/, INetCallBack callBack) {
        toPost(context, isShow, relativeUrl, mPlatform, params, iParser, callBack);
    }

    protected final void upload(Context context, boolean isShow, String relativeUrl, File file,
                                @Nullable IParser iParser/*如果不需要解析数据，传null*/, INetCallBack callBack) {
        Headers headers = headers();
        if (TextUtils.isEmpty(domain)) {
            domain = HttpStorage.DEFAULT.getDomain(context);
        }
        MultipartBody body = new MultipartBody.Builder()
                .addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("application/octet-stream"), file))
                .build();
        ResponseHandleInterface httpResponseHandler = getResponseHandleInterface(context, this,
                mPlatform, printLog, isShow, context, iParser, mGson, callBack);
        httpResponseHandler.onStart();
        mOkHttpClient.newCall(new Request.Builder()
                .url(domain + relativeUrl)
                .headers(headers)
                .post(body)
                .tag(context)
                .build()).enqueue(httpResponseHandler);
    }

    @UiThread
    protected final void toPost(Context context, boolean isShow, String relativeUrl, Platform platform, @Nullable RequestParams params,
                                @Nullable IParser iParser/*如果不需要解析数据，传null*/, INetCallBack callBack) {

        Headers headers = headers();
        String json = "";
        if (params != null) {
            json = params.toJson();

        }
        if (TextUtils.isEmpty(domain)) {
            domain = HttpStorage.DEFAULT.getDomain(context);
        }

        if (printLog) {
            Log.i(TAG, String.format("Http url-> %s%s", domain, relativeUrl));
            Log.i(TAG, "Http headers from local to server->\n" + headers.toString());
            Log.i(TAG, String.format("Http params -> %s", json));
        }

        ResponseHandleInterface httpResponseHandler = getResponseHandleInterface(context, this, platform, printLog, isShow, context, iParser, mGson, callBack);
        httpResponseHandler.onStart();
        mOkHttpClient.newCall(new Request.Builder()
                .url(domain + relativeUrl)
                .headers(headers)
                .post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json))
                .tag(context)
                .build()).enqueue(httpResponseHandler);
    }


    protected abstract Map<String, String> addHeaders();

    protected abstract ResponseHandleInterface getResponseHandleInterface(Context context,
                                                                          BaseHttpClient client,
                                                                          Platform mPlatform,
                                                                          boolean printLog,
                                                                          boolean isShow,
                                                                          Object tag,
                                                                          IParser iParser,
                                                                          Gson gson,
                                                                          INetCallBack callBack);

    public void cancelByTag(Object tag) {
        if (tag == null) return;

        for (Call runningCall :
                mOkHttpClient.dispatcher().runningCalls()) {
            if (tag.equals(runningCall.request().tag())) {
                runningCall.cancel();
            }
        }

        for (Call call :
                mOkHttpClient.dispatcher().queuedCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
    }

    public void cancelAll() {
        mOkHttpClient.dispatcher().cancelAll();
    }

    public static class Builder {
        InputStream serverCerStream;
        InputStream clientCerStream;
        String clientCerPassword;
        boolean sslEnable;
        boolean logger;
        String domain;

        public Builder() {
        }

        public Builder(InputStream serverCerStream, InputStream clientCerStream, String clientCerPassword) {
            super();
            this.serverCerStream = serverCerStream;
            this.clientCerStream = clientCerStream;
            this.clientCerPassword = clientCerPassword;
        }


        public Builder setServerCerStream(InputStream serverCerStream) {
            this.serverCerStream = serverCerStream;
            return this;
        }

        public Builder setClientCerStream(InputStream clientCerStream) {
            this.clientCerStream = clientCerStream;
            return this;
        }

        public Builder setClientCerPassword(String password) {
            clientCerPassword = password;
            return this;
        }

        public Builder setSslEnable(boolean sslEnable) {
            this.sslEnable = sslEnable;
            return this;
        }

        public Builder withLog(boolean log) {
            this.logger = log;
            return this;
        }

        public Builder domain(String domain) {
            this.domain = domain;
            return this;
        }

        public OkHttpClient build() {
            OkHttpClient.Builder builder = new OkHttpClient.Builder()
                    .readTimeout(TIMEOUT, TimeUnit.SECONDS)
                    .writeTimeout(TIMEOUT, TimeUnit.SECONDS)
                    .connectTimeout(TIMEOUT, TimeUnit.SECONDS)
                    .pingInterval(6 * 1000, TimeUnit.SECONDS)
                    .proxy(Proxy.NO_PROXY)
                    .cookieJar(new CookieJar() {
                        @Override
                        public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
                            for (Cookie cookie : cookies) {
                                if ("JSESSIONID".equals(cookie.name())) {
                                    Logger.i("save sid = " + cookie.value());
//                                    HttpStorage.DEFAULT.setDomain();
                                    AppData.DEFAULT.setSessionId(cookie.value());
                                    AppData.DEFAULT.setDomain(cookie.domain());
                                    break;
                                }

                            }
                        }

                        @Override
                        public List<Cookie> loadForRequest(HttpUrl url) {
                            List<Cookie> list = new ArrayList<>(1);
                            String sid = AppData.DEFAULT.getSessionId();
                            if (!TextUtils.isEmpty(sid)) {
                                Logger.i("locale sid = " + sid);
                                Cookie cookie = new Cookie.Builder().name("JSESSIONID")
                                        .value(AppData.DEFAULT.getSessionId())
                                        .domain(AppData.DEFAULT.getDomain())
                                        .build();
                                list.add(cookie);
                            }
                            return list;
                        }
                    });

            if (sslEnable) {
                SSLManagerUtils.sslSocketFactory(builder, serverCerStream, clientCerStream, clientCerPassword);
            }
            return builder.build();
        }
    }

}
